Skip to content

feat(watch): gameplay watcher slices reference frames into tmp - #5

Merged
jonit-dev merged 1 commit into
mainfrom
feat/gameplay-watcher
Nov 5, 2026
Merged

jonit-dev merged 1 commit into
mainfrom
feat/gameplay-watcher

Conversation

@jonit-dev

Copy link
Copy Markdown
Collaborator

What

npm run watch -- "super mario 64 triple jump" --duration 25 --frames 6 searches YouTube for a
gameplay video of a mechanic reference, downloads one segment, and slices frames plus a contact
sheet into a tmp folder for the authoring agent to look at.

[0] 00:20:29 Super Mario 64 Speedrun Movement Guide — Derian Sera
[1] 00:02:36 Triple Jump Wall Kick Tutorial - BITS — Altruren Gaming
...
Super Mario 64 Speedrun Movement Guide @ 00:02:02 +25s → 6 frames
watch: /tmp/threenative-watch-hV0S92
look: /tmp/threenative-watch-hV0S92/sheet.jpg
  • scripts/watch.mjs — one file, no dependencies beyond yt-dlp and ffmpeg on PATH.
  • Aiming: --pick N, --from (seconds, mm:ss, hh:mm:ss, or NN% of the video), --duration,
    --frames N, --at 12,35,61, --out DIR, --keep.
  • Output: frames/NN-tSECONDSs.jpg (640px), sheet.jpg (one image, all frames in order),
    watch.json (video, segment, frame times). The clip is deleted unless --keep.

Why the defaults are what they are

The first live run opened on the SM64 title screen and the file-select menu: 6 frames of Mario's
face and no mechanic. Two fixes came out of it, both in this PR: the default window opens 10% into
the video, past the intro, and seeks are relative to the segment because yt-dlp rebases a
downloaded section to zero (the absolute seek silently produced zero frames).

Verification

  • npm test 44/44, including 9 new checks in tests/watch.spec.mjs with fake yt-dlp/ffmpeg
    binaries: manifest contents, frame names, the relative seek, non-empty --out refusal.
  • Live runs against YouTube: plan-missed output above, plus --at 5,8 --keep.
  • npm run check:data — 48 catalog rows, 48 games, unchanged.

Not included

No ffmpeg-skill vendoring: it is a 42-script editing toolkit for delivering video, and this needs
-ss, -frames:v and tile. No scene-change selection (even sampling answers the need, and the
first sheet exists to decide where to look next), no YouTube API key, no vision model.

The authoring agent can read a card and still not know what the motion looks
like. `npm run watch -- "<words>"` searches YouTube, downloads one segment
with yt-dlp, and slices evenly spaced frames plus a 3-column contact sheet
into a mkdtemp folder, with watch.json naming the video, the segment and
every frame time.

Defaults learned from the first live run: the window opens 10% into the
video, because 0% is the title card and the file menu; seeks are relative to
the segment because yt-dlp rebases a downloaded section to zero.

Frames stay in tmp and never reach data/: they are someone else's footage,
while cards cite source code at the pinned SHA.
@jonit-dev
jonit-dev merged commit 442cc99 into main Nov 5, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant